package com.semidata.trp.report.report;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.semidata.rtd.core.log.Logger;
import com.semidata.trp.report.config.Config;
import com.semidata.trp.report.sort.DataProvider;
import com.semidata.trp.report.sort.MergeSort;
import com.semidata.trp.report.utils.SessionUtils;

public class NRTReport {
	private ReportData reportData = new ReportData();
	private static Config config = Config.getConfig();
	List<String> files = new ArrayList<String>();

	private Map<String, Integer> B64 = new HashMap<String, Integer>();
	private static String[] headList = { "+", "-", "0", "1", "2", "3", "4",
			"5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
			"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
			"V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h",
			"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
			"v", "w", "x", "y", "z" };

	public NRTReport() {
		for (int i = 0; i < headList.length; i++) {
			B64.put(headList[i], i);
		}
	}

	public class LogsNode {
		private String uid;
		private String logs;

		public LogsNode(String id, String log) {
			uid = id;
			logs = log;
		}

		public void setUid(String id) {
			uid = id;
		}

		public String getUid() {
			return uid;
		}

		public void setLogs(String log) {
			logs = log;
		}

		public String getLogs() {
			return logs;
		}

		public int compare(LogsNode cmpNode) {
			return uid.compareTo(cmpNode.getUid());
		}
	}

	private class LogFilesProvider implements DataProvider<LogsNode> {

		private List<LogsNode> data = new ArrayList<>();
		private int maxSize = 5000;
		private int index = 0;
		private boolean isClosed;
		private BufferedReader bReader = null;

		public LogFilesProvider(File file) {
			int lp;
			String uid, log, tmpLine;
			try {
				isClosed = false;
				while (!isClosed) {
					tmpLine = bReader.readLine();
					if (tmpLine != null) {
						lp = tmpLine.indexOf('\t');
						uid = tmpLine.substring(0, lp);
						log = tmpLine.substring(lp + 1);
						data.add(new LogsNode(uid, log));
					} else {
						bReader.close();
						bReader = null;
						isClosed = true;
						break;
					}
					if (data.size() == maxSize) {
						break;
					}
				}
				index = 0;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				Logger.log("ERROR", e.getMessage());
			}
		}

		private void fillList() {
			int lp;
			String uid, log, tmpLine;
			if (data != null)
				data.clear();
			if (bReader == null) {
				data = null;
			} else {
				while (!isClosed) {
					try {
						tmpLine = bReader.readLine();
						if (tmpLine != null) {
							lp = tmpLine.indexOf('\t');
							uid = tmpLine.substring(0, lp);
							log = tmpLine.substring(lp + 1);
							data.add(new LogsNode(uid, log));
						} else {
							bReader.close();
							bReader = null;
							isClosed = true;
							break;
						}
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						Logger.log("ERROR", e.getMessage());
					}
					if (data.size() == maxSize) {
						break;
					}
				}
			}
			index = 0;
		}

		@Override
		public LogsNode peek() {
			if (data != null && index < data.size()) {
				return data.get(index);
			} else {
				if (!isClosed) {
					fillList();
				}
				if (data == null) {
					return null;
				} else {
					return data.get(index);
				}
			}
		}

		@Override
		public LogsNode poll() {
			LogsNode tmpNode = null;
			if (data != null && index < data.size()) {
				tmpNode = data.get(index);
				index++;
				return tmpNode;
			} else {
				if (!isClosed) {
					fillList();
				}
				if (data == null) {
					return null;
				} else {
					tmpNode = data.get(index);
					index++;
					return tmpNode;
				}
			}
		}

	}

	public ReportData report(List<DataProvider<LogsNode>> providers) {
		ReportData rData = new ReportData();
		LogsNode tmpLogNode;
		MergeSort<LogsNode> sorter = new MergeSort<LogsNode>(providers,
				new Comparator<LogsNode>() {

					@Override
					public int compare(LogsNode o1, LogsNode o2) {
						return o1.compare(o2);
					}

				});
		rData.clear();
		MiniHeapNode node = new MiniHeapNode();
		String uid = null, lastUid = null;
		StringBuffer sb = new StringBuffer();
		sb.setLength(0);
		// Map<String, Integer> tmpCount = new HashMap<>();
		/* for (Map.Entry<String, String> entry : logList.entrySet()) { */
		while ((tmpLogNode = sorter.poll()) != null) {
			uid = tmpLogNode.getUid();

			// Logger.log("DEBUG", "read  : uid is " + uid + " and  log is " +
			// tmpLogNode.getLogs());

			if (uid.length() < 1)
				continue;
			// uid = URLDecoder.decode(entry.getKey(), "utf-8");
			// head = uid.substring(0, 1);
			// if (B64.get(head) != null) {
			if (lastUid == null) {
				// Logger.log("DEBUG", "first uid : " + uid);
				sb.setLength(0);
				sb.append(uid);
				sb.append("\t");
				sb.append(tmpLogNode.getLogs());
			} else {
				// Logger.log("DEBUG", "uid : " + uid + " and  last : " +
				// lastUid);
				if (uid.compareTo(lastUid) != 0) {
					// sb.append("\n");
					node.init(sb.toString());
					rData.add(node.getPersonalData());
					// Logger.log("DEBUG","add " + lastUid);
					node.clear();
					sb.setLength(0);
					sb.append(uid);
					sb.append("\t");
					sb.append(tmpLogNode.getLogs());
				} else {
					// Logger.log("DEBUG", "merge : " + sb.toString() +
					// " and " + tmpLogNode.getLogs());
					sb.append("\t");
					sb.append(tmpLogNode.getLogs());
				}
				// }
			}
			lastUid = uid;
		}
		// sb.append("\n");
		node.init(sb.toString());
		rData.add(node.getPersonalData());
		node.clear();
		return rData;
	}

	public void start(String date, int dateType) throws Exception {

		int perSize;
		int threadsNum = config.getReportThreads();
		perSize = headList.length / threadsNum;
		if (headList.length % threadsNum > 0) {
			perSize = perSize + 1;
		}

		B64.clear();
		this.reportData.clear();
		Map<Integer, List<String>> group = new HashMap<>();
		Thread[] threads = new Thread[threadsNum];
		for (int i = 0; i < threadsNum; i++) {
			List<String> tmpList = new ArrayList<>();
			for (int j = 0; j < perSize; j++) {
				if (i * perSize + j >= headList.length)
					break;
				tmpList.add(headList[i * perSize + j]);
			}
			group.put(i, tmpList);
		}

		final ReportData[] reportDatas = new ReportData[threadsNum];

		for (int i = 0; i < threads.length; i++) {
			reportDatas[i] = new ReportData();
			final int tag = i;
			final List<String> tmpTailList = group.get(i);
			threads[i] = new Thread(new Runnable() {
				public void run() {
					int p, t;
					String fileName = null;
					String tail = null;
					List<DataProvider<LogsNode>> providers = new ArrayList<>();

					for (int j = 0; j < tmpTailList.size(); j++) {
						providers.clear();
						for (int k = 0; k < files.size(); k++) {
							fileName = files.get(k);
							if (fileName.endsWith(".log")) {
								p = fileName.lastIndexOf("/");
								t = fileName.indexOf(".", p);
								p = fileName.lastIndexOf(".");
								if (p > t) {
									// Logger.log("DEBUG", "right");
									tail = fileName.substring(t + 1, p);
									if (tail.compareTo(tmpTailList.get(j)) == 0) {
										Logger.log("DEBUG", "add " + fileName);
										providers.add(new LogFilesProvider(
												new File(fileName)));
									}
								}
							}
						}
						reportDatas[tag].merge(report(providers));
						Logger.log(
								"DEBUG",
								"thread "
										+ String.valueOf(tag)
										+ " : "
										+ String.valueOf(reportDatas[tag]
												.getTotalReport().getPv()));
					}
				}
			});
			threads[i].start();
		}
		for (int i = 0; i < threadsNum; i++) {
			threads[i].join();
		}
		for (int i = 0; i < reportDatas.length; i++) {
			reportData.merge(reportDatas[i]);
			reportDatas[i] = null;
		}

		SessionUtils.update(date, dateType, reportData);
	}

	public String getFirstDayOf7Days(String date) {
		SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMdd");
		Calendar c = new GregorianCalendar();
		c.set(Integer.valueOf(date.substring(0, 4)),
				Integer.valueOf(date.substring(4, 6)) - 1,
				Integer.valueOf(date.substring(6)) - 6);
		String firstDayOf7Days = dateformat.format(c.getTime());
		return firstDayOf7Days;
	}

	public void setFileList(String date, int dateType, boolean ignore)
			throws Exception {
		String root = config.getReportDir();
		StringBuffer sb = new StringBuffer();
		sb.setLength(0);
		sb.append(root);
		sb.append("/");
		File file = new File(sb.toString());
		File[] fileList = file.listFiles();
		files.clear();
		if (dateType < 1 || dateType > 3
				|| (dateType == 1 && date.length() != 8)
				|| (dateType == 2 && date.length() != 8)
				|| (dateType == 3 && date.length() != 6)) {
			throw new Exception("error pram!");
		}

		List<String> days = new ArrayList<String>();
		if (dateType == 1) {
			for (int i = 0; i < fileList.length; i++) {
				if (fileList[i].getName().substring(0, 8).equals(date)) {
					files.add(fileList[i].getAbsolutePath());
				}
			}
		} else if (dateType == 2) {
			SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMdd");
			Calendar c = new GregorianCalendar();
			c.set(Integer.valueOf(date.substring(0, 4)),
					Integer.valueOf(date.substring(4, 6)) - 1,
					Integer.valueOf(date.substring(6)) - 7);
			for (int i = 0; i < 7; i++) {
				c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH) + 1);
				String dayOfWeek = dateformat.format(c.getTime());
				days.add(dayOfWeek);
			}
			for (int j = 0; j < fileList.length; j++) {
				if (days.indexOf(fileList[j].getName().substring(0, 8)) >= 0) {
					files.add(fileList[j].getAbsolutePath());
				}
			}
		} else {
			SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMdd");
			Calendar c = new GregorianCalendar();
			c.set(Integer.valueOf(date.substring(0, 4)),
					Integer.valueOf(date.substring(4)) - 1, 1);

			int dayOfMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);
			for (int i = 0; i < dayOfMonth; i++) {
				c.set(Calendar.DAY_OF_MONTH, i + 1);
				String day = dateformat.format(c.getTime());
				days.add(day);
			}
			for (int j = 0; j < fileList.length; j++) {
				if (days.indexOf(fileList[j].getName().substring(0, 8)) >= 0) {
					files.add(fileList[j].getAbsolutePath());
				}
			}
		}

		// starttest();
		start(date, dateType);
		clear();
	}

	private void clear() {
		// TODO Auto-generated method stub
		files.clear();
		reportData.clear();
	}
	
	public static void main(String[] args) {
	}

}
